package com.cgs.query.config

import com.cgs.query.configuration.QueryConfiguration
import com.cgs.query.find.QueryDefineFindHandler
import com.cgs.query.find.SourceFindHandlerImpl
import com.cgs.query.handler.DefineFindHandler
import com.cgs.query.handler.SourceFindHandler
import com.cgs.query.service.*
import com.cgs.query.service.impl.*
import com.cgs.query.util.Utils
import mu.KotlinLogging
import org.apache.commons.lang3.StringUtils
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.context.ApplicationListener
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration
import org.springframework.context.event.ContextRefreshedEvent
import org.springframework.data.jpa.repository.config.EnableJpaRepositories
import org.springframework.jdbc.core.JdbcTemplate
import org.springframework.jdbc.datasource.init.DataSourceInitializer
import org.springframework.orm.jpa.JpaTransactionManager
import org.springframework.orm.jpa.JpaVendorAdapter
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter
import org.springframework.transaction.PlatformTransactionManager
import java.util.concurrent.atomic.AtomicBoolean
import javax.annotation.Resource
import javax.sql.DataSource

private val logger = KotlinLogging.logger { }

@Configuration
@EnableJpaRepositories(
        basePackages = ["com.cgs.query.service.jpa"],
        entityManagerFactoryRef = "queryEntityManagerFactory",
        transactionManagerRef = "queryTransactionManager")
@ComponentScan("com.cgs.query.service.impl")
open class JpaConfiguration : ApplicationListener<ContextRefreshedEvent> {

    private val isInit = AtomicBoolean(true)

    @Resource(name = "sql.runner.queryDS")
    private lateinit var dataSource: DataSource

    @Bean
    @ConfigurationProperties("sql.runner")
    open fun jpaProperties(): JpaProperties {
        return JpaProperties()
    }

    @Bean
    @ConfigurationProperties("sql.runner")
    open fun jpaCustomProperties(): JpaCustomProperties {
        return JpaCustomProperties()
    }

    @Bean
    @ConditionalOnMissingBean
    open fun jpaVendorAdapter(): JpaVendorAdapter {
        val adapter = HibernateJpaVendorAdapter()
        adapter.setShowSql(jpaProperties().isShowSql)
        adapter.setDatabase(jpaProperties().determineDatabase(this.dataSource))
        adapter.setDatabasePlatform(jpaProperties().databasePlatform)
        adapter.setGenerateDdl(jpaProperties().isGenerateDdl)
        return adapter
    }

    @Bean
    open fun queryEntityManagerFactory(): LocalContainerEntityManagerFactoryBean {
        val factory = LocalContainerEntityManagerFactoryBean()
        factory.dataSource = dataSource
        factory.setPackagesToScan("com.cgs.query.domain")
        factory.jpaVendorAdapter = jpaVendorAdapter()
        factory.jpaPropertyMap = jpaProperties().properties + jpaProperties().getHibernateProperties(dataSource)
        return factory
    }

    @Bean
    open fun queryTransactionManager(): PlatformTransactionManager {
        return JpaTransactionManager(queryEntityManagerFactory().getObject())
    }

    @Bean
    open fun quasarDataSourceInitializer(): DataSourceInitializer {
        val dsInitializer = DataSourceInitializer()
        dsInitializer.setDataSource(dataSource)
        return dsInitializer
    }

    @Bean
    @ConditionalOnMissingBean()
    open fun defaultSourceFindHandler(querySourceService: IQuerySourceService): SourceFindHandler = SourceFindHandlerImpl(querySourceService).apply { logger.info { "创建SourceFindHandlerImpl" } }

    @Bean
    @ConditionalOnMissingBean()
    open fun defaultDefineFindHandler(queryDefineService: IQueryDefineService, executionObjService: IExecutionObjService): DefineFindHandler =
            QueryDefineFindHandler(queryDefineService, executionObjService).apply { logger.info { "创建DefineFindHandler" } }

    override fun onApplicationEvent(event: ContextRefreshedEvent?) {

        val applicationContext = event!!.applicationContext
        Utils.nullAndThen(applicationContext.parent) {
            val jdbcTemplateAware = applicationContext!!.getBeansOfType(RunnerJdbcTemplateAware::class.java)

            val jdbcTemplate = JdbcTemplate(dataSource)
            jdbcTemplateAware.forEach { _, bean -> bean.setRunnerJdbcTemplate(jdbcTemplate) }

            val jpaCustomPropertiesAware = applicationContext.getBeansOfType(JpaCustomPropertiesAware::class.java)
            jpaCustomPropertiesAware.forEach { _, bean -> bean.setJpaCustomProperties(jpaCustomProperties()) }

            val queryConfigurationAware = applicationContext.getBeansOfType(QueryConfigurationAware::class.java)
            val queryConfiguration = applicationContext.getBean(QueryConfiguration::class.java)
            queryConfigurationAware.forEach { _, bean -> bean.setQueryConfiguration(queryConfiguration) }
        }
    }
}